#! /usr/bin/env python
# -*- coding:utf-8 -*-
import pandas as pd
import numpy as np
import configparser
import os, sys
cur_path = sys.path[0]
sys.path.append(cur_path)
import mysql_conn
from xgboost.sklearn import XGBRegressor
import pickle
from sklearn import linear_model
config = configparser.ConfigParser(allow_no_value=True)
cur_path = os.getcwd()
import load_info as load
import logging
logging.basicConfig(level=logging.DEBUG, #日志级别为DEBUG
                    format="%(asctime)s %(pathname)s %(levelname)s %(lineno)d %(message)s", #日志格式设置
                    datefmt = '%Y-%m-%d  %H:%M:%S %a',    #日期格式设置
                    filename = os.getcwd()+'/common/logging.log')  #日志输入文件地址
"""
回归模型定义 目前含xgboost 和linear两种模型
结构：
    class model_xgboost    #xgboost模型
    class model_linear     #linear模型
说明：非执行文件
"""
class model_xgboost:
    """
    xgb模型
    """
    def __init__(self,model_path,data_path,dev_code):
        self.model_path = model_path #模型全路径
        self.data_path = data_path   #数据集全路径
        self.dev_code = dev_code     #设备code
    #获取模型参数
    def get_param(self):
        """
        获取模型参数
        :return: 模型参数：dict,key是参数名称,value是参数值
        """
        ini_path = cur_path + '/model/model_%s/model_%s.ini'%(self.dev_code, self.dev_code)  #定制model.ini文件地址
        if not os.path.exists(ini_path):
            ini_path = cur_path + '/common/model.ini'#公用model.ini文件地址
        config.read(ini_path, "utf-8")
        param_dict = {}
        for i in config['model_param']:
            para_i = config['model_param'][i]
            if para_i.isdigit():
                para_i = int(para_i)
            elif para_i.replace(".", "").isdigit():
                para_i = float(para_i)
            param_dict[i] = para_i
        return param_dict
    #训练
    def xgb_train(self):
        """
        训练模型并将模型写入模型文件
        :return:train_data:df,训练集
        """
        if not os.path.exists(os.path.abspath(os.path.join(self.model_path, ".."))):
            os.mkdir(os.path.abspath(os.path.join(self.model_path, "..")))
        if not os.path.exists(os.path.abspath(os.path.join(self.data_path, ".."))):
            os.mkdir(os.path.abspath(os.path.join(self.data_path, "..")))
        #获取参数字典
        param_dict = self.get_param()
        #读取训练集并训练模型
        train_data = pd.read_csv(self.data_path, encoding="utf-8")
        X = train_data[[i for i in train_data.columns if i not in ["data", "sdate"]]]
        Y = train_data["data"]
        ##定义模型
        xgb_model = XGBRegressor(**param_dict)
        xgb_model.fit(X, Y)
        ##训练结果写入模型文件
        with open(self.model_path, "wb") as f:
            pickle.dump(xgb_model, f)
        pre_data = xgb_model.predict(X)
        train_data["pre"] = pre_data.astype(int)
        return train_data
    #预测
    def xgb_predict(self):
        """
        预测
        :return:test_data:df,预测集
        """
        #读取测试集和模型,进行预测
        if not os.path.exists(os.path.abspath(os.path.join(self.data_path, ".."))):
            os.mkdir(os.path.abspath(os.path.join(self.data_path, "..")))
        test_data = pd.read_csv(self.data_path, encoding="utf-8")
        X = test_data[[i for i in test_data.columns if i not in ["data", "sdate"]]]
        with open(self.model_path, "rb") as f:
            gcv = pickle.load(f)
        pre_data = gcv.predict(X)
        test_data["data"] = pre_data.astype(int)
        return test_data

class model_linear:
    """
    一元线性回归模型
    """
    def __init__(self,graph_id,energy_code,conn_file='mysql_conn.cnf'):
        self.conn_file = conn_file  #mysql连接信息文件名
        self.graph_id = graph_id  #当前图谱ID
        self.energy_code = energy_code #能量的dev_code
        lg = load.Graph_load()
        self.work_start = lg.load_graph_worktime(self.graph_id)["start_work_time"]
    #生成训练数据集
    def get_train_data(self):
        """
        获取所有当前图谱历史真实数据,生成训练集
        :return:train_x：df,训练集特征
                train_y：df,训练集标签
        """
        #根据图谱开始结束日期获取真实数据
        db = mysql_conn.db_con()
        ##获取图谱的结束月日,开始月日
        sqlstr = "select graph_end_time as e_t,graph_start_time as s_t from graph_info where graph_id = %s"
        graph_date = pd.read_sql(sqlstr, db, params=[self.graph_id]).values[0]
        graph_date = [i for i in graph_date]  # ["04-09","10-20"] 4月9日,10月20日
        if self.graph_id == 13:
            sqlstr1 =  "select * from data_rt where date_format(sdate, '%%m-%%d') >= %s or date_format(sdate, '%%m-%%d') <= %s and dev_code = %s"
        else:
            sqlstr1 = r"select * from data_rt where date_format(sdate, '%%m-%%d') >= %s and date_format(sdate, '%%m-%%d') <= %s  and dev_code = %s"
        data = pd.read_sql(sqlstr1, db, params=[graph_date[1], graph_date[0],self.energy_code])  # 获取训练数据标签及日期
        db.close()
        #生成训练集
        data["hour"] = [1 if i < self.work_start else 0 for i in pd.to_datetime(data['sdate']).dt.hour.values]  # 八点前（图谱上班时间）标识1,否则是0
        data['sdate'] = pd.to_datetime(data['sdate']).dt.date  # sdate变成日期
        df_group = data.groupby(['sdate', "hour"], as_index=False).sum()  # 按每日八点前和非八点前将数据求和
        df_group = df_group.pivot_table(index='sdate', columns='hour', values='data')  # 将八点前和非八点前的日数据变成列,sdate变成index
        df_group.reset_index(inplace=True)  # 重置index
        df_group["data"] = df_group[0] + df_group[1]  # data 等于八点前与非八点前的和
        df_group.drop("sdate", axis=1, inplace=True)  # 删除日期列
        df_group.drop([0], axis=1, inplace=True)  # 删除八点后列
        # 过滤掉标准化值大于2.3的异常值
        df_group["label_zscore"] = [abs(i - np.mean(df_group["data"])) / np.std(df_group["data"]) for i in  df_group["data"]]
        df_group = df_group.loc[df_group["label_zscore"] <= 2.3]
        df_group.drop("label_zscore", axis=1, inplace=True)
        #得到训练集
        train_x = df_group[[i for i in df_group.columns if i not in [1]]]
        train_y = df_group[1]
        return train_x, train_y
    #训练
    def linear_train(self,train_x, train_y):
        """
        线性回归模型训练并保存模型
        :param:train_x:df,训练集特征集
        :param:train_y:df,训练集标签集
        """
        model_file = cur_path + r"/common/Before_work_%s.pkl"%(self.graph_id)
        model = linear_model.LinearRegression()
        model.fit(train_x, train_y)
        with open(model_file, "wb") as f:
            pickle.dump(model, f)
    #预测八点前需求量
    def linear_predict(self,target):
        """
        预测八点前需求量
        :param: target： int或float 一日的能量预测值
        :return:
            pre_data[0]：float ,八点前能量需求
        """
        model_file = cur_path + r"/common/Before_work_%s.pkl"%(self.graph_id)
        if not os.path.exists(model_file):
            train_x, train_y = self.get_train_data()
            self.linear_train(train_x, train_y)
        with open(model_file, "rb") as f:
            model = pickle.load(f)
        test_x = pd.DataFrame([target], columns=["data"])
        pre_data = model.predict(test_x)
        return pre_data[0]


if __name__ == '__main__':
    model_xgboost(1,1,'sada.YC999').get_param()